home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 3
/
Cream of the Crop 3.iso
/
comm
/
wnos5src.zip
/
AX25ROUT.C
< prev
next >
Wrap
Text File
|
1993-10-05
|
55KB
|
2,258 lines
/* Low level AX.25 frame processing - address header and routing */
/* WAMPES parts by DK5SG modified and ported to WNOS by DB3FL */
/* DAMA parts by DL1BKE modified and ported to WNOS by DB3FL */
#include <stdio.h>
#include <ctype.h>
#include "global.h"
#include "config.h"
#ifdef AX25
#include "socket.h"
#include "mbuf.h"
#include "iface.h"
#include "timer.h"
#include "arp.h"
#include "slip.h"
#include "ax25.h"
#ifdef NETROM
#include "netrom.h"
#endif
#include "ip.h"
#include "tcp.h"
#include "trace.h"
#include "files.h"
#include "icmp.h"
#define axptr(a) ((struct ax25_addr *) (a))
#define next_seq(n) (((n) + 1) & MMASK)
#define T1_TIME 3000
char Mycall[AXALEN] = "\0";
char Nomycall[] = "Mycall not set\n";
static int MHwait = 0; /* Semaphore to serialize access to heardlist */
#ifdef NETROM
extern void nr_derate __ARGS((struct ax25_cb *axp));
#endif
struct ax25_cb *Ax25_cb = NULLAX25;
struct iface *axroute_default_ifp = NULLIF;
struct axroute_tab *Axroute_tab = 0;
int t1_timeout __ARGS((struct ax25_cb *cp));
static void near axroute __ARGS((struct ax25_cb *axp,struct mbuf *bp));
static void near axproto_recv __ARGS((struct iface *ifp,struct mbuf *bp,char repeated));
static void near setaxstate __ARGS((struct ax25_cb *axp,int newstate));
static void near logaddr __ARGS((struct iface *iface,char *addr,char pid));
/* Default AX.25 parameters */
int16 T1init = 10; /* Retransmission timeout */
int16 T2init = 2; /* Acknowledgement delay timeout */
int16 T3init = 600; /* keep-alive polling */
int16 T4init = 60; /* Busy timeout */
int16 T5init = 1; /* Packet assembly timeout */
int16 Maxframe = 2; /* Stop and wait */
int16 Retries = 10; /* 10 retries */
int16 Axwindow = 2048; /* 2K incoming text before RNR'ing */
int16 Paclen = 256; /* 256-byte I fields */
int16 Pthresh = 64; /* Send polls for packets larger than this */
int16 Digipeat = 2; /* Controls digipeating */
int T3disc = 1; /* 0 = polling, 1 = disconnecting */
/* List of AX.25 multicast addresses in network format (shifted ascii).
* Only the first entry is used for transmission, but an incoming
* packet with any one of these destination addresses is recognized
* as a multicast.
* DON'T CHANGE THE SEQUENCE !!!!!!!!!
*/
char Ax25multi[][AXALEN] = {
'Q'<<1, 'S'<<1, 'T'<<1, ' '<<1, ' '<<1, ' '<<1, '0'<<1, /* QST */
'N'<<1, 'O'<<1, 'D'<<1, 'E'<<1, 'S'<<1, ' '<<1, '0'<<1, /* NODES */
'I'<<1, 'D'<<1, ' '<<1, ' '<<1, ' '<<1, ' '<<1, '0'<<1, /* ID */
'M'<<1, 'A'<<1, 'I'<<1, 'L'<<1, ' '<<1, ' '<<1, '0'<<1, /* MAIL */
'O'<<1, 'P'<<1, 'E'<<1, 'N'<<1, ' '<<1, ' '<<1, '0'<<1, /* OPEN */
'C'<<1, 'Q'<<1, ' '<<1, ' '<<1, ' '<<1, ' '<<1, '0'<<1, /* CQ */
'B'<<1, 'E'<<1, 'A'<<1, 'C'<<1, 'O'<<1, 'N'<<1, '0'<<1, /* BEACON */
'R'<<1, 'M'<<1, 'N'<<1, 'C'<<1, ' '<<1, ' '<<1, '0'<<1, /* RMNC */
'A'<<1, 'L'<<1, 'L'<<1, ' '<<1, ' '<<1, ' '<<1, '0'<<1, /* ALL */
'F'<<1, 'L'<<1, 'X'<<1, 'N'<<1, 'E'<<1, 'T'<<1, '0'<<1, /* FLXNET */
'L'<<1, '3'<<1, 'R'<<1, 'T'<<1, 'T'<<1, ' '<<1, '0'<<1, /* L3RTT */ /* bke 920712 */
'\0',
};
/* New-style frame segmenter. Returns queue of segmented fragments, or
* original packet if small enough
*/
#ifdef NETROM
struct mbuf *
#else
static struct mbuf *
#endif
segmenter(
struct mbuf *bp, /* Complete packet */
int16 ssize) /* Max size of frame segments */
{
struct mbuf *bp1, *bptmp, *result = NULLBUF;
int16 len = len_p(bp), offset = 0;
int segments;
/* See if packet is too small to segment. Note 1-byte grace factor
* so the PID will not cause segmentation of a 256-byte IP datagram.
*/
if(len <= ssize + 1) {
/* Too small to segment */
return bp;
}
ssize -= 2; /* ssize now equal to data portion size */
segments = 1 + (len - 1) / ssize; /* # segments */
while(segments != 0) {
offset += dup_p(&bptmp,bp,offset,ssize);
if(bptmp == NULLBUF) {
free_q(&result);
break;
}
/* Make room for segmentation header */
bp1 = pushdown(bptmp,2);
bp1->data[0] = PID_SEGMENT;
bp1->data[1] = --segments;
if(offset == ssize)
bp1->data[1] |= SEG_FIRST;
enqueue(&result,bp1);
}
free_p(bp);
return result;
}
/* Send IP datagrams across an AX.25 link */
int
ax_send(struct mbuf *bp,struct iface *iface,int32 gateway,int prec,int del,int tput,int rel)
{
char *hw_addr;
struct ax25_cb *axp, *axp1;
struct mbuf *tbp;
int16 mode;
struct ip bp1;
struct arp_tab *arp;
struct connection conn;
struct tcp seg;
struct tcb *tcb;
if(gateway == iface->broadcast) /* This is a broadcast IP datagram */
return (*iface->output)(iface,Ax25multi[0],iface->hwaddr,PID_IP,bp);
if((arp = arp_lookup(ARP_AX25,gateway)) != NULLARP && arp->state == ARP_VALID) {
hw_addr = arp->hw_addr;
mode = arp->flags;
} else if((hw_addr = res_arp(iface,ARP_AX25,gateway,bp)) == NULLCHAR) {
return 0; /* Wait for address resolution */
}
if(del || (!rel && (mode == DATAGRAM_MODE)) || addreq(hw_addr,Ax25multi[0])) {
mode = DATAGRAM_MODE;
}
if(mode == DATAGRAM_MODE) {
/* Use UI frame */
return (*iface->output)(iface,hw_addr,iface->hwaddr,PID_IP,bp);
}
if((axp = find_ax25(hw_addr,iface->hwaddr)) == NULLAX25) {
/* Open a new connection */
if((axp1 = open_ax25(iface,iface->hwaddr,hw_addr,
AX_ACTIVE,s_arcall,s_atcall,s_ascall,-2)) == NULLAX25) {
goto quit;
}
axp1->mode = DGRAM;
}
if(axp == NULLAX25) {
axp = axp1;
axp->user = 0;
if(axp->state == DISCONNECTED) {
setaxstate(axp,CONNECTING);
}
}
dup_p(&tbp,bp,0,len_p(bp));
if (ntohip(&bp1,&tbp) != -1) {
if (ntohtcp(&seg,&tbp) != -1) {
conn.local.port = seg.source;
conn.local.address = bp1.source;
conn.remote.port = seg.dest;
conn.remote.address = bp1.dest;
if((tcb = lookup_tcb(&conn)) != NULLTCB && len_p(tbp) > 0
&& tcb->state != TCP_TIME_WAIT) {
set_timer(&tcb->timer,10 * dur_timer(&tcb->timer));
if(dur_timer(&tcb->timer) > 300000L)
set_timer(&tcb->timer,300000L);
start_timer(&tcb->timer);
}
}
}
free_p(tbp);
/* discard IP frames and send a SOURCE_QUENCH if there already are bytes
* in the txq - DC0HK.920401 */
if(len_p(axp->txq) > axp->iface->mtu) {
ntohip(&bp1,&bp);
icmp_output(&bp1,bp,ICMP_QUENCH,0,NULL);
goto quit;
}
tbp = pushdown(bp,1);
/* Insert the PID */
tbp->data[0] = (mode == IPCAM_MODE) ? PID_NO_L3 : PID_IP;
/* Look for segmentation */
if((bp = segmenter(tbp,axp->iface->flags->paclen)) != NULLBUF) {
send_ax25(axp,bp,DGRAM);
return 0;
}
quit:
free_p(bp);
return -1;
}
/* Add header and send connectionless (UI) AX.25 packet.
* Note that the calling order here must match enet_output
* since ARP also uses it. */
int
ax_output(
struct iface *iface, /* Interface to use; overrides routing table */
char *dest, /* Destination AX.25 address (7 bytes, shifted) */
char *source, /* Source AX.25 address (7 bytes, shifted) */
int16 pid, /* Protocol ID */
struct mbuf *data) /* Data field (follows PID) */
{
struct mbuf *abp;
struct ax25_cb axp;
char path[3*AXALEN], *cp;
memcpy(path,dest,AXALEN);
path[ALEN] &= ~E;
memcpy(path + AXALEN,source,AXALEN);
path[ALEN + AXALEN] |= E;
addrcp(axp.path + AXALEN,axp.iface->hwaddr);
build_path(&axp,NULLIF,path,0);
axp.iface = iface;
axp.path[ALEN + AXALEN] &= ~C;
/* Allocate mbuf for control and PID fields, and fill in */
abp = pushdown(data,axp.pathlen + 2);
memcpy(abp->data,axp.path,axp.pathlen);
cp = abp->data + axp.pathlen;
*cp++ = UI;
*cp = pid;
axroute(&axp,abp);
return 0;
}
struct axroute_tab *
axroute_tabptr(struct ax25_addr *call,int create)
{
struct axroute_tab *rp = 0;
for(rp = Axroute_tab;
rp && !addreq((char *)&rp->call,(char *)call);
rp = rp->next) ;
if(!rp && create) {
rp = mxallocw(sizeof(struct axroute_tab));
rp->call = *call;
if(Axroute_tab == 0 || strcmp((char *)&rp->call,(char *)&Axroute_tab->call) < 0) {
rp->next = Axroute_tab;
Axroute_tab = rp;
} else {
struct axroute_tab *rrp, *rptmp;
for(rrp = Axroute_tab; rrp; rrp = rrp->next) {
if(strcmp((char *)&rp->call,(char *)&rrp->call) > 0) {
if(rrp->next == (struct axroute_tab *)0) {
rrp->next = rp;
rp->next = (struct axroute_tab *)0;
break;
}
rptmp = rrp;
continue;
}
rptmp->next = rp;
rp->next = rrp;
break;
}
}
}
return rp;
}
void
axroute_add(struct ax25_cb *cp,int perm)
{
int i, ncalls = 0;
char *ap;
struct axroute_tab *rp, *lastnode = 0;
struct ax25_addr calls[MAXDIGIS + 1];
for (ap = cp->path + AXALEN; !addreq(ap,cp->iface->hwaddr); ap += AXALEN) ;
do {
ap += AXALEN;
if (ap >= cp->path + cp->pathlen) {
ap = cp->path;
}
if (!*ap || addreq(ap,cp->iface->hwaddr)) {
return;
}
for (i = 0; i < ncalls; i++) {
if (addreq((char *) (calls + i), ap)) {
return;
}
}
calls[ncalls++] = *axptr(ap);
} while (ap != cp->path);
for (i = 0; i < ncalls; i++) {
rp = axroute_tabptr(calls + i, 1);
if (perm || !rp->perm) {
if (lastnode) {
rp->digi = lastnode;
rp->ifp = 0;
} else {
rp->digi = 0;
rp->ifp = cp->iface;
}
rp->perm = perm;
}
rp->time = currtime;
lastnode = rp;
}
}
static void near
axroute(struct ax25_cb *cp,struct mbuf *bp)
{
char *dest;
struct axroute_tab *rp;
struct iface *ifp;
if (cp && cp->iface) {
ifp = cp->iface;
} else {
if (bp->data[AXALEN + ALEN] & E) {
dest = bp->data;
} else {
for (dest = bp->data + 2 * AXALEN; ; dest += AXALEN) {
if (!(dest[ALEN] & REPEATED)) {
break;
}
if (dest[ALEN] & E) {
dest = bp->data;
break;
}
}
}
rp = axroute_tabptr(axptr(dest),0);
ifp = (rp && rp->ifp) ? rp->ifp : axroute_default_ifp;
}
if (ifp) {
if (ifp->forw) {
ifp = ifp->forw;
}
ifp->rawsndcnt++;
ifp->lastsent = secclock();
(*ifp->raw)(ifp, bp);
} else {
free_p(bp);
}
return;
}
static void near
send_packet(struct ax25_cb *cp,int type,int cmdrsp,struct mbuf *data)
{
int control = type;
struct mbuf *bp = alloc_mbuf(cp->pathlen + 1);
char *p = bp->data;
memcpy(p, cp->path, cp->pathlen);
if (cmdrsp & DST_C) p[ALEN] |= C;
if (cmdrsp & SRC_C) p[ALEN + AXALEN] |= C;
p += cp->pathlen;
if (type == I) {
control |= (cp->vs << 1);
cp->vs = next_seq(cp->vs);
}
if ((type & 3) != U) {
control |= (cp->vr << 5);
stop_timer(&cp->t2);
}
if (cmdrsp & PF) {
control |= PF;
}
*p++ = control;
if (type == RR || type == REJ || type == UA) {
cp->rnrsent = 0;
}
if (type == RNR) {
cp->rnrsent = 1;
}
if (type == REJ) {
cp->rejsent = 1;
}
if (cmdrsp == POLL) {
cp->polling = 1;
}
if (type == I || cmdrsp == POLL) {
start_timer(&cp->t1);
}
if (type == I) {
start_timer(&cp->t3);
}
bp->cnt = p - bp->data;
bp->next = data;
axroute(cp, bp);
}
void
ax_recv(struct iface *iface,struct mbuf *bp)
{
char axheader[10*AXALEN+1], (*mpp)[AXALEN], *ap, *cntrlptr;
int addrsize, pid, multicast = 0;
struct ax25_cb axp;
if (!(bp && bp->data)) goto discard;
if (is_bud(bp->data + AXALEN,0)) goto discard;
for (cntrlptr = bp->data; !(*cntrlptr++ & E); ) ;
addrsize = (int)(cntrlptr - bp->data);
if(addrsize < 2 * AXALEN
|| addrsize >= bp->cnt
|| addrsize > 10 * AXALEN
|| addrsize % AXALEN) goto discard;
if(addrsize == 2 * AXALEN) {
logaddr(iface,bp->data + AXALEN,*cntrlptr + 1);
} else {
for(ap = bp->data + 2 * AXALEN; ap < cntrlptr; ap += AXALEN) {
if(!(ap[ALEN] & REPEATED)) {
logaddr(iface,ap - AXALEN,*cntrlptr + 1);
if(!addreq(ap,iface->hwaddr))
goto discard;
ap[ALEN] |= REPEATED;
switch(iface->flags->digipeat) {
case 2:
axproto_recv(iface,bp,0);
return;
case 1:
axroute(NULLAX25,bp);
return;
default:
goto discard;
}
}
}
logaddr(iface,ap - AXALEN,*cntrlptr + 1);
}
if((*cntrlptr & ~PF) != UI) {
/* NREX bke 920716 */
#ifdef NETROM
if(ismyNcall(bp->data)) {
axproto_recv(iface,bp,1);
return;
}
#endif
/* bke */
if(!addreq(bp->data,iface->hwaddr)
&& (find_ax25(bp->data + AXALEN,bp->data) == NULLAX25))
goto discard;
axproto_recv(iface,bp,1);
return;
}
/* NREX bke 920716 */
#ifdef NETROM
if(ismyNcall(bp->data) || addreq(bp->data,iface->hwaddr)) {
goto jump;
}
#else
/* bke */
if(addreq(bp->data,iface->hwaddr)) {
goto jump;
}
#endif
/* Examine immediate destination for a multicast address */
for(mpp = Ax25multi;(*mpp)[0] != '\0';mpp++)
if(addreq(bp->data,*mpp)) {
multicast = 1;
goto jump;
}
goto discard;
jump:
pullup(&bp, axheader, addrsize + 1);
pid = PULLCHAR(&bp);
if (!bp) return;
if(!multicast)
build_path(&axp,iface,axheader,REVERSE);
switch (pid) {
case PID_IP:
if(bp->cnt >= 20) {
struct arp_tab *arp;
if((arp = arp_add( /* src_ipaddr */
get32(&bp->data[12]),ARP_AX25,axheader + AXALEN,0,0)) != NULLARP) {
stop_timer(&arp->timer);
set_timer(&arp->timer,0L);
}
}
ip_route(iface,iface,bp,multicast);
return;
case PID_ARP:
arp_input(iface,bp);
return;
#ifdef NETROM
case PID_NETROM:
nr_nodercv(iface,axheader + AXALEN,bp);
return;
#endif
#ifdef AX25
case PID_NO_L3: /* FLEXNET-Search should now be answered */
if(!multicast) {
send_packet(&axp,DM,FINAL,NULLBUF);
}
#endif
default:
goto discard;
}
discard:
free_p(bp);
}
#ifdef AXIP
static int32 axipaddr[NAX25]; /* table of IP addresses of AX.25 interfaces */
/*
* FCS lookup table as generated by fcsgen.c
*/
static int16 near fcstab[256] = {
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};
/* raw routine for sending AX.25 on top of IP */
static int
axip_raw(
struct iface *iface, /* Pointer to interface control block */
struct mbuf *bp) /* Data field */
{
int16 len = len_p(bp), fcs = 0xffff;
struct mbuf *bp1;
if(dup_p(&bp1,bp,0,len) != len) {
free_p(bp);
return -1;
}
while (len--) { /* calculate FCS */
fcs = (fcs >> 8) ^ fcstab[(fcs ^ PULLCHAR(&bp1)) & 0x00ff];
}
fcs ^= 0xffff; /* final FCS (is this right?) */
bp1 = alloc_mbuf(sizeof(int16));
*bp1->data = fcs & 0xff;
*(bp1->data+1) = (fcs >> 8) & 0xff;
bp1->cnt += sizeof(int16);
append(&bp,bp1);
return ip_send(INADDR_ANY,axipaddr[iface->dev],AX25_PTCL,0,0,bp,0,0,0);
}
/* Handle AX.25 frames received inside IP according to RFC-1226 */
void
axip_input(
struct iface *iface, /* Input interface */
struct ip *ip, /* IP header */
struct mbuf *bp, /* AX.25 frame with FCS */
int rxbroadcast) /* Accepted for now */
{
int i;
struct mbuf *tbp;
int16 len, f, fcs = 0xffff;
/* Since the AX.25 frame arrived on an interface that does
not necessarily support AX.25, we have to find a suitable
AX.25 interface, or drop the packet.
*/
/* Try to find a matching AX.25 pseudo interface */
for(i = 0; i < NAX25; ++i) {
if(axipaddr[i] == ip->source) {
break;
}
}
if(i == NAX25) {
/* Here we could still try to pick a real AX.25 interface,
* but that would mean that we are accepting AX.25 frames
* from unknown IP hosts, so we'd rather drop it.
*/
free_p(bp);
return;
}
for(iface = Ifaces; iface != NULLIF; iface = iface->next) {
if(iface->raw == axip_raw && iface->dev == i) {
/* found the right AX.25 pseudo interface */
break;
}
}
if(iface == NULLIF) {
free_p(bp);
return;
}
iface->rawrecvcnt++;
iface->lastrecv = secclock();
len = len_p(bp) - sizeof(int16);
if(dup_p(&tbp,bp,0,len) != len) {
free_p(bp);
return;
}
while(len--) {
fcs = (fcs >> 8) ^ fcstab[(fcs ^ PULLCHAR(&bp)) & 0x00ff];
}
fcs ^= 0xffff;
f = PULLCHAR(&bp);
f |= (PULLCHAR(&bp) << 8);
if(fcs == f) {
ax_recv(iface,tbp);
} else {
free_p(tbp);
}
}
#endif /* AXIP */
static void near
reset_t1(struct ax25_cb *cp)
{
int32 tmp = 4 * cp->mdev + cp->srt;
if(tmp > T1_TIME * cp->iface->flags->t1init) {
tmp = T1_TIME * cp->iface->flags->t1init;
}
if(tmp < T1_TIME) {
tmp = T1_TIME;
}
set_timer(&cp->t1,tmp);
}
static int near
space_ax25(struct ax25_cb *cp)
{
if(cp) {
switch (cp->state) {
case CONNECTING:
case CONNECTED:
if(!cp->closed) {
int cnt = cp->iface->flags->axwindow - len_p(cp->txq);
if(cnt > 0) {
return cnt;
}
}
}
}
return 0;
}
static int near
busy(struct ax25_cb *cp)
{
if(cp->peer) {
return (space_ax25(cp->peer) == 0);
} else {
return (len_p(cp->rxq) >= cp->iface->flags->axwindow);
}
}
static void near
send_ack(struct ax25_cb *cp,int cmdrsp)
{
int type;
if(busy(cp)) {
type = RNR;
} else if ( /* !cp->rejsent && */
(cp->reseq[0].bp || cp->reseq[1].bp || cp->reseq[2].bp ||
cp->reseq[3].bp || cp->reseq[4].bp || cp->reseq[5].bp ||
cp->reseq[6].bp || cp->reseq[7].bp)) {
type = REJ;
} else {
type = RR;
}
send_packet(cp,type,cmdrsp,NULLBUF);
}
static void near
try_send(struct ax25_cb *cp,int fill_sndq,int mode)
{
stop_timer(&cp->t5);
while(cp->unack < cp->cwind) {
struct mbuf *bp, *tbp;
int cnt;
if(cp->state != CONNECTED || cp->remotebusy) {
return;
}
if(fill_sndq && cp->t_upcall) {
if((cnt = space_ax25(cp)) > 0) {
(*cp->t_upcall)(cp, cnt);
if (cp->unack >= cp->cwind) {
return;
}
}
}
if(!cp->txq) {
/* DAMA */
if(cp->dama && cp->rxasm) {
t1_timeout(cp);
}
return;
}
if(mode == STREAM) {
cnt = len_p(cp->txq);
if(cnt < cp->iface->flags->paclen) {
if (cp->unack) {
return;
} else if(!cp->dama) {
int32 i = cp->sndqtime + cp->iface->flags->t5init * 1000L;
int32 j = msclock();
if (!cp->peer && i > j) {
set_timer(&cp->t5,i - j);
start_timer(&cp->t5);
return;
}
}
} else {
cnt = cp->iface->flags->paclen;
}
bp = alloc_mbuf(cnt);
bp->cnt = cnt;
pullup(&cp->txq,bp->data,bp->cnt);
tbp = pushdown(bp,1);
tbp->data[0] = PID_NO_L3;
bp = tbp;
} else {
bp = dequeue(&cp->txq);
}
enqueue(&cp->rxasm, bp);
cp->unack++;
cp->sndtime[cp->vs] = msclock();
dup_p(&bp, bp, 0, MAXINT16);
send_packet(cp, I, CMD, bp);
}
return;
}
static void near
setaxstate(struct ax25_cb *cp,int newstate)
{
int oldstate = cp->state;
if(oldstate == TIMEWAIT) {
del_ax25(cp);
return;
}
cp->state = newstate;
cp->polling = 0;
cp->retries = 0;
stop_timer(&cp->t1);
stop_timer(&cp->t2);
stop_timer(&cp->t4);
stop_timer(&cp->t5);
if(!cp->dama) {
reset_t1(cp);
}
switch (newstate) {
case DISCONNECTED:
if (cp->peer) {
disc_ax25(cp->peer);
}
if (cp->s_upcall) {
(*cp->s_upcall)(cp, oldstate, newstate);
}
if (cp->peer && cp->peer->state == DISCONNECTED) {
del_ax25(cp->peer);
cp->peer = cp->peer->peer = NULLAX25;
}
break;
case CONNECTING:
if (cp->s_upcall) {
(*cp->s_upcall)(cp, oldstate, newstate);
}
send_packet(cp, SABM, POLL, NULLBUF);
break;
case CONNECTED:
if (cp->peer && cp->peer->state == DISCONNECTED) {
send_packet(cp->peer, UA, FINAL, NULLBUF);
setaxstate(cp->peer, CONNECTED);
}
if (cp->s_upcall) {
(*cp->s_upcall)(cp, oldstate, newstate);
}
try_send(cp, 1, cp->mode);
break;
case DISCONNECTING:
if (cp->peer) {
disc_ax25(cp->peer);
cp->peer = cp->peer->peer = NULLAX25;
}
if (cp->s_upcall) {
(*cp->s_upcall)(cp, oldstate, newstate);
}
send_packet(cp, DISC, POLL, NULLBUF);
break;
}
}
int
t1_timeout(struct ax25_cb *cp)
{
if(!cp->dama) {
int32 tmp = (dur_timer(&cp->t1) * 5 + 2) / 4;
if(tmp > T1_TIME * cp->iface->flags->t1init) {
tmp = T1_TIME * cp->iface->flags->t1init;
}
if(tmp < T1_TIME) {
tmp = T1_TIME;
}
set_timer(&cp->t1,tmp);
cp->cwind = 1;
}
cp->retries++;
switch (cp->state) {
case DISCONNECTED:
break;
case CONNECTING:
if (cp->peer && cp->peer->state == DISCONNECTED) {
if (cp->retries > 2) {
cp->reason = LB_TIMEOUT;
setaxstate(cp, DISCONNECTED);
#ifdef NETROM
nr_derate(cp);
#endif
} else {
start_timer(&cp->t1);
}
} else if (cp->retries > cp->iface->flags->retries) {
cp->reason = LB_TIMEOUT;
setaxstate(cp, DISCONNECTED);
#ifdef NETROM
nr_derate(cp);
#endif
} else {
send_packet(cp, SABM, POLL, NULLBUF);
}
break;
case CONNECTED:
if(cp->retries > (cp->iface->flags->retries * 2)) {
cp->reason = LB_TIMEOUT;
setaxstate(cp, DISCONNECTED);
#ifdef NETROM
nr_derate(cp);
#endif
} else if((!cp->polling && !cp->remotebusy && cp->unack
&& (len_p(cp->rxasm) - 1) <= cp->iface->flags->pthresh)
|| cp->dama) {
struct mbuf *bp;
int old_vs = cp->vs;
cp->vs = (cp->vs - cp->unack) & 7;
cp->sndtime[cp->vs] = 0;
dup_p(&bp, cp->rxasm, 0, MAXINT16);
send_packet(cp, I, CMD, bp);
cp->vs = old_vs;
} else {
send_ack(cp, POLL);
}
break;
case DISCONNECTING:
if (cp->retries > (cp->iface->flags->retries / 2)) {
cp->reason = LB_TIMEOUT;
setaxstate(cp, DISCONNECTED);
#ifdef NETROM
nr_derate(cp);
#endif
} else {
send_packet(cp, DISC, POLL, NULLBUF);
}
break;
}
return 0;
}
static void
t2_timeout(struct ax25_cb *cp)
{
send_ack(cp, RESP);
}
static void
t3_timeout(struct ax25_cb *cp)
{
if(cp->dama) {
cp->reason = LB_TIMEOUT;
setaxstate(cp,DISCONNECTED);
#ifdef NETROM
nr_derate(cp);
#endif
return;
}
if(!run_timer(&cp->t1) && cp->state == CONNECTED) {
if(cp->iface->flags->t3disc) {
disc_ax25(cp);
} else {
send_ack(cp, POLL);
}
}
}
static void
t4_timeout(struct ax25_cb *cp)
{
if (!cp->polling) send_ack(cp, POLL);
}
static void
t5_timeout(struct ax25_cb *cp)
{
try_send(cp, 1, STREAM);
}
/* Look up entry in connection table */
struct ax25_cb *
find_ax25(char *remote,char *local)
{
struct ax25_cb *axp, *axlast = NULLAX25;
/* Search list */
for(axp = Ax25_cb; axp != NULLAX25; axlast = axp, axp = axp->next){
if(addreq(axp->path,remote) && addreq(axp->path + AXALEN,local)){
if(axlast != NULLAX25) {
/* Move entry to top of the list */
axlast->next = axp->next;
axp->next = Ax25_cb;
Ax25_cb = axp;
}
return axp;
}
}
return NULLAX25;
}
static void near
init_timer(struct ax25_cb *axp)
{
if(axp->iface != NULLIF) {
set_timer(&axp->t1,axp->iface->flags->t1init * 1000L);
axp->t1.func = (void (*) __ARGS((void *))) t1_timeout;
axp->t1.arg = axp;
set_timer(&axp->t2,axp->iface->flags->t2init * 1000L);
axp->t2.func = (void (*) __ARGS((void *))) t2_timeout;
axp->t2.arg = axp;
set_timer(&axp->t3,axp->iface->flags->t3init * 1000L);
axp->t3.func = (void (*) __ARGS((void *))) t3_timeout;
axp->t3.arg = axp;
set_timer(&axp->t4,axp->iface->flags->t4init * 1000L);
axp->t4.func = (void (*) __ARGS((void *))) t4_timeout;
axp->t4.arg = axp;
set_timer(&axp->t5,axp->iface->flags->t5init * 1000L);
axp->t5.func = (void (*) __ARGS((void *))) t5_timeout;
axp->t5.arg = axp;
}
}
/* Create an ax25 control block. Allocate a new structure, if necessary,
* and fill it with all the defaults. The caller
* is still responsible for filling in the reply address
*/
static struct ax25_cb * near
cr_ax25(char *remote,char *local,struct iface *iface)
{
struct ax25_cb *axp;
if(remote == NULLCHAR)
return NULLAX25;
if((axp = find_ax25(remote,local)) == NULLAX25){
/* Not already in table; create an entry */
axp = mxallocw(sizeof(struct ax25_cb));
axp->next = Ax25_cb;
Ax25_cb = axp;
}
axp->state = DISCONNECTED;
axp->cwind = 1;
axp->user = -1;
axp->iface = iface;
init_timer(axp);
/* Always to a receive and state upcall as default */
/* Also bung in a default transmit upcall - in case */
axp->r_upcall = s_arcall;
axp->t_upcall = s_atcall;
axp->s_upcall = s_ascall;
axp->peer = NULLAX25;
return axp;
}
/* Open an AX.25 connection */
struct ax25_cb *
open_ax25(
struct iface *iface, /* Interface */
char *local, /* Local address */
char *remote, /* Remote address */
int mode, /* active/passive/server */
void (*r_upcall)(), /* Receiver upcall handler */
void (*t_upcall)(), /* Transmitter upcall handler */
void (*s_upcall)(), /* State-change upcall handler */
int user) /* User linkage area */
{
struct ax25_cb *axp = NULLAX25;
char remtmp[AXALEN], path[3*AXALEN];
if(remote == NULLCHAR){
remote = remtmp;
setcall(remote," ");
}
if((axp = find_ax25(remote,local)) != NULLAX25 && axp->state != DISCONNECTED)
return NULLAX25; /* Only one to a customer */
if(axp == NULLAX25 && (axp = cr_ax25(remote,local,iface)) == NULLAX25)
return NULLAX25;
axp->r_upcall = r_upcall;
axp->t_upcall = t_upcall;
axp->s_upcall = s_upcall;
axp->user = (user == -2) ? -1 : user;
memcpy(path,remote,AXALEN);
path[ALEN] &= ~E;
memcpy(path + AXALEN,local,AXALEN);
path[ALEN + AXALEN] |= E;
switch(mode) {
case AX_SERVER:
axp->clone = 1;
case AX_PASSIVE: /* Note fall-thru */
axp->state = LISTEN;
return axp;
case AX_ACTIVE:
break;
}
build_path(axp,NULLIF,path,NEWSRT);
if(!axp->iface) {
del_ax25(axp);
return NULLAX25;
}
if(user == -2) {
addrcp(axp->path + AXALEN,axp->iface->hwaddr);
axp->path[axp->pathlen - 1] |= E;
axroute_add(axp,0);
}
switch(axp->state){
case DISCONNECTED:
if(user != -2)
setaxstate(axp,CONNECTING);
break;
case CONNECTING:
free_q(&axp->txq);
free_q(&axp->rxasm);
break;
case DISCONNECTING: /* Ignore */
break;
case CONNECTED:
free_q(&axp->txq);
setaxstate(axp,CONNECTING);
break;
}
return axp;
}
int
send_ax25(struct ax25_cb *cp,struct mbuf *bp,int pid)
{
int16 cnt;
if (!(cp && bp)) {
free_p(bp);
return (-1);
}
cp->mode = pid;
switch (cp->state) {
case DISCONNECTED:
free_p(bp);
return (-1);
case CONNECTING:
case CONNECTED:
if (!cp->closed) {
if ((cnt = len_p(bp)) != 0) {
if (pid == 0) {
append(&cp->txq, bp);
} else {
enqueue(&cp->txq, bp);
}
cp->sndqtime = msclock();
if(!cp->dama) {
try_send(cp, 0, pid);
}
}
return (int)cnt;
}
case DISCONNECTING:
free_p(bp);
return (-1);
}
return (-1);
}
/* Receive incoming data on an AX.25 connection */
struct mbuf *
recv_ax25(struct ax25_cb *axp,int16 cnt)
{
struct mbuf *bp;
if(!axp->rxq)
return NULLBUF;
if(cnt == 0 || axp->mode == DGRAM || axp->rcvcnt <= cnt){
/* This means we want it all */
bp = dequeue(&axp->rxq);
cnt = len_p(bp);
axp->rxq = NULLBUF;
} else {
bp = alloc_mbuf(cnt);
bp->cnt = pullup(&axp->rxq,bp->data,cnt);
}
axp->rcvcnt -= cnt;
/* If this has un-busied us, send a RR to reopen the window */
if(axp->rnrsent && !busy(axp)) {
send_ack(axp,RESP);
}
return bp;
}
/* Close an AX.25 connection */
int
disc_ax25(struct ax25_cb *axp)
{
if(axp == NULLAX25 || axp->closed) {
return -1;
}
axp->closed = 1;
switch(axp->state){
case LISTEN:
del_ax25(axp);
case DISCONNECTED:
case DISCONNECTING:
return -1;;
case CONNECTED:
if(!axp->txq && !axp->unack) {
setaxstate(axp,DISCONNECTING);
}
return 0;
case CONNECTING:
setaxstate(axp, DISCONNECTED);
return 0;
}
return -1;
}
/* Abruptly terminate an AX.25 connection */
int
reset_ax25(struct ax25_cb *axp)
{
void (*upcall)();
if (axp == NULLAX25) {
return -1;
}
upcall = axp->s_upcall;
axp->reason = LB_DM;
setaxstate(axp,DISCONNECTED);
/* Clean up if the standard upcall isn't in use */
if(upcall != s_ascall) {
del_ax25(axp);
}
return 0;
}
/* Remove entry from connection table */
void
del_ax25(struct ax25_cb *conn)
{
struct ax25_cb *axp, *axlast = NULLAX25;
for(axp = Ax25_cb; axp != NULLAX25; axlast = axp, axp = axp->next) {
if(axp == conn) {
break;
}
}
if(axp == NULLAX25) {
return; /* Not found */
}
if(axp->state != TIMEWAIT) {
int i;
/* Timers should already be stopped, but just in case... */
stop_timer(&axp->t1);
stop_timer(&axp->t2);
stop_timer(&axp->t3);
stop_timer(&axp->t4);
stop_timer(&axp->t5);
/* Free allocated resources */
for(i = 0; i < 8; i++) {
free_p(axp->reseq[i].bp);
}
free_q(&axp->txq);
free_q(&axp->rxasm);
free_q(&axp->rxq);
free_q(&axp->segasm);
axp->state = TIMEWAIT;
axp->t5.func = (void (*) __ARGS((void *))) del_ax25;
axp->t5.arg = axp;
set_timer(&axp->t5,30000);
start_timer(&axp->t5);
return;
}
stop_timer(&axp->t5);
/* Remove from list */
if(axlast != NULLAX25) {
axlast->next = axp->next;
} else {
Ax25_cb = axp->next;
}
xfree(axp);
}
/*
* setcall - convert callsign plus substation ID of the form
* "KA9Q-0" to AX.25 (shifted) address format
* Address extension bit is left clear
* Return -1 on error, 0 if OK
*/
int
setcall(char *out,char *call)
{
int csize, i;
unsigned ssid;
char c, *dp;
if(out == NULLCHAR || call == NULLCHAR || *call == '\0')
return -1;
/* Find dash, if any, separating callsign from ssid
* Then compute length of callsign field and make sure
* it isn't excessive
*/
if((dp = strchr(call,'-')) == NULLCHAR)
csize = strlen(call);
else
csize = (int)(dp - call);
if(csize > ALEN)
return -1;
/* Now find and convert ssid, if any */
if(dp != NULLCHAR) {
dp++; /* skip dash */
ssid = atoi(dp);
if(ssid > 15)
return -1;
} else
ssid = 0;
/* Copy upper-case callsign, left shifted one bit */
for(i = 0; i < csize; i++) {
c = *call++;
if(islower(c))
c = toupper(c);
*out++ = c << 1;
}
/* Pad with shifted spaces if necessary */
for(; i < ALEN; i++)
*out++ = ' ' << 1;
/* Insert substation ID field and set reserved bits */
*out = 0x60 | (ssid << 1);
*out |= E;
return 0;
}
int
addreq(char *a,char *b)
{
if (*a++ != *b++) return 0;
if (*a++ != *b++) return 0;
if (*a++ != *b++) return 0;
if (*a++ != *b++) return 0;
if (*a++ != *b++) return 0;
if (*a++ != *b++) return 0;
return (*a & SSID) == (*b & SSID);
}
void
addrcp(char *to,char *from)
{
*to++ = *from++;
*to++ = *from++;
*to++ = *from++;
*to++ = *from++;
*to++ = *from++;
*to++ = *from++;
*to = (*from & SSID) | 0x60;
}
int
callreq(char *a)
{
struct iface *ifp;
for(ifp = Ifaces; ifp; ifp = ifp->next) {
if(ifp->output == ax_output) {
if(addreq(a,ifp->hwaddr)) {
return 1;
}
}
}
return 0;
}
/* Convert encoded AX.25 address to printable string */
char *
pax25(char *e,char *addr)
{
int i;
char c, *cp = e;
for(i = ALEN; i != 0; i--) {
c = (*addr++ >> 1) & 0x7f;
if(c != ' ')
*cp++ = c;
}
if ((*addr & SSID) != 0) {
sprintf(cp,"-%d",(*addr >> 1) & 0xf); /* ssid */
} else {
*cp = '\0';
}
return e;
}
static int near
put_reseq(struct ax25_cb *cp,struct mbuf *bp,int ns)
{
char *p;
int cnt, sum;
struct axreseq *rp;
struct mbuf *tp;
if (next_seq(ns) == cp->vr) return 0;
rp = &cp->reseq[ns];
if (rp->bp) return 0;
for (sum = 0, tp = bp; tp; tp = tp->next) {
cnt = tp->cnt;
p = tp->data;
while (cnt--) sum += uchar(*p++);
}
if (ns != cp->vr && sum == rp->sum) return 0;
rp->bp = bp;
rp->sum = sum;
return 1;
}
void
build_path(struct ax25_cb *cp,struct iface *ifp,char *newpath,int flags)
{
char buf[10*AXALEN];
int len, ndigi;
char *ap, *tp, *myaddr = 0;
struct axroute_tab *rp = 0;
/*** find address length and copy address into control block ***/
for (ap = newpath; !(ap[ALEN] & E); ap += AXALEN) ;
cp->pathlen = (int)(ap - newpath + AXALEN);
if (flags & REVERSE) {
addrcp(cp->path, newpath + AXALEN);
addrcp(cp->path + AXALEN, newpath);
for (tp = cp->path + 2 * AXALEN;
tp < cp->path + cp->pathlen;
tp += AXALEN, ap -= AXALEN) {
addrcp(tp, ap);
}
} else {
memcpy(cp->path, newpath, cp->pathlen);
}
/*** store iface pointer into control block ***/
cp->iface = ifp;
/*** save address for autorouting ***/
if(cp->iface && (flags & REVERSE)) {
/* NREX bke 920717 replace NR-Call with the Call of this iface */
/* then add the route to the list and rename */
/* the target to the original call */
#ifdef NETROM
int k = ismyNcall(cp->path + AXALEN);
if(k) {
addrcp(cp->path + AXALEN, ifp->hwaddr);
}
#endif
axroute_add(cp,0);
#ifdef NETROM
if(k) {
addrcp(cp->path + AXALEN, Nrifaces[k-1].call);
}
#endif
/* bke * a bit confusing, isn't it? */
}
/*** find my digipeater address (use the last one) ***/
for (ap = cp->path + 2 * AXALEN; ap < cp->path + cp->pathlen; ap += AXALEN) {
if(!ifp) {
for (ifp = Ifaces; ifp; ifp = ifp->next) {
if (ifp->output == ax_output && addreq(ifp->hwaddr,ap)) {
break;
}
}
}
if(addreq(ap,ifp->hwaddr)) {
myaddr = ap;
}
}
if(!(flags & REVERSE)) {
/*** autorouting, remove all digipeaters before me ***/
if (myaddr && myaddr > cp->path + 2 * AXALEN) {
len = (int)((cp->path + cp->pathlen) - myaddr);
memcpy(buf, myaddr, len);
myaddr = cp->path + 2 * AXALEN;
memcpy(myaddr, buf, len);
cp->pathlen = 2 * AXALEN + len;
}
/*** add necessary digipeaters and find interface ***/
ap = myaddr ? myaddr + AXALEN : cp->path + 2 * AXALEN;
rp = axroute_tabptr(axptr((ap >= cp->path + cp->pathlen) ? cp->path : ap), 0);
for (; rp; rp = rp->digi) {
if (rp->digi && cp->pathlen < sizeof(cp->path)) {
len = (int)((cp->path + cp->pathlen) - ap);
if (len) {
memcpy(buf, ap, len);
}
addrcp(ap, (char *) &rp->digi->call);
if (len) {
memcpy(ap + AXALEN, buf, len);
}
cp->pathlen += AXALEN;
}
cp->iface = rp->ifp;
}
if(!cp->iface) {
cp->iface = axroute_default_ifp;
}
/*** replace my address with hwaddr of interface ***/
if(myaddr != 0) {
addrcp(cp->pathlen == 2 * AXALEN ? cp->path + AXALEN : cp->path + 2 * AXALEN,
cp->iface ? cp->iface->hwaddr : Mycall);
}
}
/*** clear all address bits ***/
for (ap = cp->path; ap < cp->path + cp->pathlen; ap += AXALEN) {
ap[ALEN] = (ap[ALEN] & SSID) | 0x60;
}
/*** set REPEATED bits for all digipeaters before and including me ***/
if (myaddr) {
for (ap = cp->path + 2 * AXALEN; ap <= myaddr; ap += AXALEN) {
ap[ALEN] |= REPEATED;
}
}
/*** mark end of address field ***/
cp->path[cp->pathlen-1] |= E;
if(flags & NEWSRT && !cp->dama) {
init_timer(cp);
/*** estimate round trip time ***/
if (myaddr) {
ndigi = (int)((cp->path + cp->pathlen - myaddr) / AXALEN) + 1;
} else {
ndigi = cp->pathlen / AXALEN;
}
cp->srt = (500 * cp->iface->flags->t1init * ndigi);
cp->mdev = 0;
reset_t1(cp);
}
}
int
is_bud(char *icall,int store)
{
struct btable_t {
struct btable_t *next;
char call[AXALEN];
} ;
static struct btable_t *btable, *p, **tp;
/* this is necessary to ignore any ssid */
char bcall[AXALEN];
memcpy(bcall,icall,AXALEN);
bcall[ALEN] = '0' << 1;
tp = &btable;
for (p = *tp; p && !addreq(p->call, bcall); p = p->next) ;
if (!p && store) {
p = mxallocw(sizeof(struct btable_t));
addrcp(p->call,bcall);
p->next = *tp;
*tp = p;
}
return (int) (p != 0);
}
static int near
is_flexnet(char *call,int store)
{
struct ftable_t {
struct ftable_t *next;
char call[AXALEN];
};
static struct ftable_t *ftable, *p, **tp;
tp = &ftable;
for (p = *tp; p && !addreq(p->call, call); p = p->next) ;
if (!p && store) {
p = mxallocw(sizeof(struct ftable_t));
addrcp(p->call, call);
p->next = *tp;
*tp = p;
}
return (int) (p != 0);
}
static void near
axproto_recv(struct iface *ifp,struct mbuf *bp,char repeated)
{
struct ax25_cb *cp, *cpp;
int cmdrsp, control, for_me, type, pid, ipcam;
char *cntrlptr,
dama_flag = DAMA - (bp->data[AXALEN+ALEN] & DAMA); /* DAMA bke 920531 */
for(cntrlptr = bp->data + AXALEN;!(cntrlptr[ALEN] & E);cntrlptr += AXALEN) ;
cntrlptr += AXALEN;
control = uchar(*cntrlptr);
if (control & 1) {
if (control & 2) {
type = control & ~PF;
} else {
type = control & 0xf;
}
} else {
type = I;
}
for_me = addreq(bp->data,ifp->hwaddr);
#ifdef NETROM
/* NREX bke 920716 Nr-Call ist mein Call... */
if (!for_me && ismyNcall(bp->data)) {
for_me = 1;
}
#endif
/* bke */
if((cp = find_ax25(bp->data + AXALEN,bp->data)) != NULLAX25 && !for_me) {
for_me = 2;
if(type == SABM && repeated) {
free_p(bp);
return;
}
}
if (!for_me && (type == UI || addreq(bp->data, bp->data + AXALEN)
|| is_flexnet(bp->data, 0) || is_flexnet(bp->data + AXALEN, 0))) {
axroute(NULLAX25, bp);
return;
}
if((pid = uchar(cntrlptr[1])) == PID_FLEXNET) {
is_flexnet(bp->data + AXALEN, 1); /* */
}
if ((bp->data[ALEN] & C) == (bp->data[ALEN + AXALEN] & C)) {
cmdrsp = VERS1;
} else {
cmdrsp = ((bp->data[ALEN] & C) ? DST_C : SRC_C) | (control & PF);
}
if(cp) {
if (for_me == 1 || (for_me == 2 && cp->peer != NULLAX25)) {
/* build_path(cp,ifp,bp->data,REVERSE);
} else {*/
char temp[AXALEN];
addrcp(temp,bp->data); /* if the frame is not directly for */
addrcp(bp->data,ifp->hwaddr); /* us, but for a user of our gateway */
build_path(cp,ifp,bp->data,REVERSE); /* we MUST change the adress field to */
addrcp(cp->path + AXALEN,temp); /* to make axrouting work! */
if(cp->pathlen == 2 * AXALEN) { /* DB3FL.910110 */
cp->path[AXALEN + ALEN] |= E; /* set address field end bit */
}
}
cpp = cp->peer;
} else {
cp = cr_ax25(bp->data + AXALEN,bp->data,ifp);
build_path(cp, ifp, bp->data, REVERSE | NEWSRT);
if (!for_me) {
cp->peer = cpp = cr_ax25(bp->data,bp->data + AXALEN,NULLIF);
cpp->peer = cp;
build_path(cpp, NULLIF, bp->data, NEWSRT);
} else {
cpp = NULLAX25;
}
}
if (type == SABM) {
int i;
build_path(cp, ifp, bp->data, REVERSE);
if (cp->unack) {
start_timer(&cp->t1);
} else {
stop_timer(&cp->t1);
}
stop_timer(&cp->t2);
stop_timer(&cp->t4);
cp->polling = 0;
cp->rnrsent = 0;
cp->rejsent = 0;
cp->remotebusy = 0;
cp->vr = 0;
cp->vs = cp->unack;
cp->retries = 0;
for (i = 0; i < 8; i++) {
if (cp->reseq[i].bp) {
free_q(&cp->reseq[i].bp);
cp->reseq[i].bp = NULLBUF;
}
}
}
/* do it every time a frame is recveied to get defined timer values */
if(dama_flag) {
cp->dama = dama_flag;
stop_timer(&cp->t1);
stop_timer(&cp->t5);
if(cp->state != CONNECTING && cp->state != DISCONNECTING) {
set_timer(&cp->t1,0);
}
set_timer(&cp->t2,5000);
set_timer(&cp->t5,0);
start_timer(&cp->t3);
cp->cwind = 7;
cp->polling = 0;
cp->retries = 0;
}
ipcam = 0;
if (type == I) {
/* IPCAM-feature - DB3FL.910104 */
if((for_me == 1 || (for_me == 2 && cp->peer == NULLAX25))
&& pid == PID_NO_L3
&& uchar(cntrlptr[2]) == 0x45
&& uchar(cntrlptr[3]) == 0x00
&& uchar(cntrlptr[4]) < 0x02) {
cntrlptr[1] = PID_IP;
pid = PID_IP;
ipcam = 1;
}
cp->mode = (pid == PID_NO_L3) ? STREAM : DGRAM;
if(cpp) cpp->mode = cp->mode;
if(pid == PID_IP) {
struct arp_tab *arp;
if ((arp = arp_add( /* src_ipaddr */
get32(&cntrlptr[14]),ARP_AX25,cp->path,0,1+ipcam)) != NULLARP) {
stop_timer(&arp->timer);
set_timer(&arp->timer,0L);
}
}
start_timer(&cp->t3);
}
/* */
//tprintf("state %d, name %s, forme %d \n",cp->state,cp->iface->name,for_me);
switch (cp->state) {
case DISCONNECTED:
if (for_me == 1 || (for_me == 2 && cp->peer == NULLAX25)) {
if (type == SABM && cmdrsp != VERS1 && cp->r_upcall) {
send_packet(cp, UA, (cmdrsp == POLL) ? FINAL : RESP, NULLBUF);
setaxstate(cp, CONNECTED);
start_timer(&cp->t3);
} else {
if (cmdrsp != RESP && cmdrsp != FINAL) {
send_packet(cp, DM, (cmdrsp == POLL) ? FINAL : RESP, NULLBUF);
}
del_ax25(cp);
}
} else {
if (type == SABM && cmdrsp != VERS1 && cpp->state == DISCONNECTED) {
setaxstate(cpp, CONNECTING);
} else if (type == SABM && cmdrsp != VERS1 && cpp->state == CONNECTING) {
build_path(cpp, NULLIF, bp->data, NEWSRT);
send_packet(cpp, SABM, POLL, NULLBUF);
} else {
if (cmdrsp != RESP && cmdrsp != FINAL) {
send_packet(cp, DM, (cmdrsp == POLL) ? FINAL : RESP, NULLBUF);
}
if (cpp->state == DISCONNECTED) {
del_ax25(cpp);
del_ax25(cp);
}
}
}
break;
case CONNECTING:
switch (type) {
case I:
case RR:
case RNR:
case REJ:
break;
case SABM:
if (cmdrsp != VERS1) {
send_packet(cp, UA, (cmdrsp == POLL) ? FINAL : RESP, NULLBUF);
setaxstate(cp, CONNECTED);
}
break;
case UA:
if (cmdrsp != VERS1) {
setaxstate(cp, CONNECTED);
start_timer(&cp->t3);
} else {
if (cpp && cpp->state == DISCONNECTED) {
send_packet(cpp, DM, FINAL, NULLBUF);
}
cp->reason = LB_DM;
setaxstate(cp, DISCONNECTING);
}
break;
case DISC:
send_packet(cp, DM, (cmdrsp == POLL) ? FINAL : RESP, NULLBUF);
case DM:
case FRMR:
if (cpp && cpp->state == DISCONNECTED)
send_packet(cpp, DM, FINAL, NULLBUF);
cp->reason = LB_DM;
setaxstate(cp, DISCONNECTED);
#ifdef NETROM
nr_derate(cp); /* TEST */
#endif
break;
}
break;
case CONNECTED:
switch (type) {
case I:
case RR:
case RNR:
case REJ: {
int nr = control >> 5;
stop_timer(&cp->t1);
if (((cp->vs - nr) & 7) < cp->unack) {
if (!cp->polling && !cp->dama) {
cp->retries = 0;
if (cp->sndtime[(nr-1)&7]) {
int32 rtt = msclock() - cp->sndtime[(nr-1)&7];
int32 abserr = rtt > cp->srt ? rtt - cp->srt : cp->srt - rtt;
cp->srt = ((AGAIN-1) * cp->srt + rtt + (AGAIN/2)) / AGAIN;
cp->mdev = ((DGAIN-1) * cp->mdev + abserr + (DGAIN/2)) >> 3;
reset_t1(cp);
}
cp->mdev += (cp->srt / cp->cwind) >> 3;
if (cp->cwind < cp->iface->flags->maxframe) {
cp->cwind++;
} else {
cp->cwind = cp->iface->flags->maxframe;
}
}
while (((cp->vs - nr) & 7) < cp->unack) {
cp->rxasm = free_p(cp->rxasm);
cp->unack--;
}
if (cpp && cpp->rnrsent && !busy(cpp)) {
send_ack(cpp, RESP);
}
}
if (type == I) {
int ns = (control >> 1) & 7;
pullup(&bp, NULLCHAR, cntrlptr - bp->data + 1 + (cp->mode == STREAM));
if (!bp) {
bp = alloc_mbuf(0);
}
if (put_reseq(cp, bp, ns)) {
int processed;
while ((bp = cp->reseq[cp->vr].bp) != NULLBUF) {
cp->reseq[cp->vr].bp = NULLBUF;
cp->vr = next_seq(cp->vr);
cp->rejsent = 0;
processed = 0;
if(for_me == 1 && uchar(cntrlptr[1]) != PID_NO_L3) {
int seg;
processed = 1;
pid = PULLCHAR(&bp);
if(cp->segremain != 0) {
/* Reassembly in progress; continue */
seg = PULLCHAR(&bp);
if(pid == PID_SEGMENT && (seg & SEG_REM) == cp->segremain - 1) {
/* Correct, in-order segment */
append(&cp->segasm,bp);
if((cp->segremain = (seg & SEG_REM)) == 0) {
/* Done; kick it upstairs */
bp = cp->segasm;
cp->segasm = NULLBUF;
cp->segremain = 0;
pid = PULLCHAR(&bp);
switch(pid) {
case PID_IP:
ip_route(ifp,ifp,bp,0);
break;
#ifdef AX25
case PID_ARP:
arp_input(ifp,bp);
break;
#endif
#ifdef NETROM
case PID_NETROM:
nr_route(bp,cp);
break;
#endif
}
}
} else {
/* Error! */
free_q(&cp->segasm);
cp->segasm = NULLBUF;
cp->segremain = 0;
free_p(bp);
}
} else {
/* No reassembly in progress */
if(pid == PID_SEGMENT) {
/* Start reassembly */
seg = PULLCHAR(&bp);
if(!(seg & SEG_FIRST)) {
free_p(bp); /* not first seg - error! */
} else {
/* Put first segment on list */
cp->segremain = seg & SEG_REM;
cp->segasm = bp;
}
} else {
/* Normal frame; send upstairs */
switch(pid) {
case PID_IP:
ip_route(ifp,ifp,bp,0);
break;
#ifdef AX25
case PID_ARP:
arp_input(ifp,bp);
break;
#endif
#ifdef NETROM
case PID_NETROM:
nr_route(bp,cp);
break;
#endif
}
}
}
}
if(!processed) {
if (for_me && cpp == NULLAX25) {
cp->rcvcnt += len_p(bp);
if(cp->user == 0 || Axi_sock == -1) {
free_p(bp);
} else {
if (cp->mode == STREAM) {
append(&cp->rxq, bp);
} else {
enqueue(&cp->rxq, bp);
}
}
} else {
send_ax25(cpp, bp, cp->mode);
}
}
}
}
if (cmdrsp == POLL) {
if(cp->dama) {
try_send(cp,1,cp->mode);
}
send_ack(cp, FINAL);
} else {
if(!cp->iface->flags->t2init) {
send_ack(cp, RESP);
} else {
set_timer(&cp->t2,cp->iface->flags->t2init * 1000L);
start_timer(&cp->t2);
}
}
if (cp->r_upcall && cp->rcvcnt) {
(*cp->r_upcall)(cp, cp->rcvcnt);
}
} else {
if (cmdrsp == POLL) {
if(cp->dama) {
try_send(cp,1,cp->mode);
}
send_ack(cp, FINAL);
}
if (cp->polling && cmdrsp == FINAL) {
cp->retries = cp->polling = 0;
}
if (type == RNR) {
if (!cp->remotebusy) {
cp->remotebusy = currtime;
}
set_timer(&cp->t4,cp->iface->flags->t4init * 1000L);
start_timer(&cp->t4);
cp->cwind = 1;
} else {
cp->remotebusy = 0;
stop_timer(&cp->t4);
if (cp->unack && type == REJ) {
struct mbuf *bp1;
int old_vs = cp->vs;
cp->vs = (cp->vs - cp->unack) & 7;
cp->sndtime[cp->vs] = 0;
dup_p(&bp1, cp->rxasm, 0, MAXINT16);
send_packet(cp, I, CMD, bp1);
cp->vs = old_vs;
cp->cwind = 1;
} else if (cp->unack && cmdrsp == FINAL) {
struct mbuf *bp1, *qp;
cp->vs = (cp->vs - cp->unack) & 7;
for (qp = cp->rxasm; qp; qp = qp->anext) {
cp->sndtime[cp->vs] = 0;
dup_p(&bp1, qp, 0, MAXINT16);
send_packet(cp, I, CMD, bp1);
}
}
}
}
try_send(cp, 1, cp->mode);
if (cp->polling || cp->unack && !cp->remotebusy) {
start_timer(&cp->t1);
}
if (cp->closed && !cp->txq && !cp->unack
|| cp->remotebusy && cp->remotebusy + 900l < currtime) {
setaxstate(cp, DISCONNECTING);
}
break;
}
case SABM:
send_packet(cp, UA, (cmdrsp == POLL) ? FINAL : RESP, NULLBUF);
try_send(cp, 1, cp->mode);
break;
case DISC:
send_packet(cp, DM, (cmdrsp == POLL) ? FINAL : RESP, NULLBUF);
case DM:
setaxstate(cp, DISCONNECTED);
break;
case UA:
cp->remotebusy = 0;
stop_timer(&cp->t4);
if (cp->unack) {
start_timer(&cp->t1);
}
try_send(cp, 1, cp->mode);
break;
case FRMR:
setaxstate(cp, DISCONNECTING);
break;
}
break;
case DISCONNECTING:
switch (type) {
case I:
case RR:
case RNR:
case REJ:
case SABM:
if (cmdrsp != RESP && cmdrsp != FINAL) {
send_packet(cp, DM, (cmdrsp == POLL) ? FINAL : RESP, NULLBUF);
}
break;
case DISC:
send_packet(cp, DM, (cmdrsp == POLL) ? FINAL : RESP, NULLBUF);
case DM:
case UA:
case FRMR:
setaxstate(cp, DISCONNECTED);
break;
}
break;
}
free_p(bp);
return;
}
#ifdef AXIP
static int
axip_stop(struct iface *iface,int tmp)
{
axipaddr[iface->dev] = 0;
return 0;
}
/* attach a fake AX.25 interface for AX.25 on top of IP */
/* argv[0] == "axip"
* argv[1] == name of new interface
* argv[2] == MTU
* argv[3] == hostname of remote end of wormhole
* argv[4] == optional callsign is not necessary when using WNOS
*/
int
axip_attach(int argc,char **argv,void *p)
{
int i;
struct iface *ifp;
if(if_lookup(argv[1]) != NULLIF) {
tprintf(Ifexist,argv[1]);
return -1;
}
for(i = 0; i < NAX25; ++i) {
if(axipaddr[i] == 0) {
break;
}
}
if(i == NAX25) {
tprintf("Max %d AXIP ifaces\n",NAX25);
return -1;
}
if((axipaddr[i] = resolve(argv[3])) == 0) {
tprintf(Badhost,argv[3]);
return -1;
}
ifp = mxallocw(sizeof(struct iface));
ifp->dev = i;
ifp->addr = Ip_addr;
ifp->name = strxdup(argv[1]);
ifp->mtu = atoi(argv[2]);
setencap(ifp,"AX25");
ifp->hwaddr = strxdup(Mycall);
ifp->raw = axip_raw;
ifp->stop = axip_stop;
init_maxheard(ifp);
init_flags(ifp);
ifp->niface = Niface++;
ifp->next = Ifaces;
Ifaces = ifp;
return 0;
}
#endif /* AXIP */
/* AX25 log functions */
void
init_maxheard(struct iface *ifp)
{
ifp->Hmax = MAXDEFAULT;
ifp->Hcurrent = 0;
ifp->lq = cxallocw(MAXDEFAULT,sizeof(struct lq));
}
int
maxheard(struct iface *ifp,int num)
{
struct lq *lnew = cxallocw(num,sizeof(struct lq)), *lold = ifp->lq;
semwait(&MHwait,1); /* request/wait */
ifp->lq = lnew;
ifp->Hcurrent = 0;
ifp->Hmax = num;
semrel(&MHwait);
xfree(lold);
return(0);
}
static struct lq * near
al_lookup(struct iface *ifp,char *addr)
{
int i;
struct lq *lp = ifp->lq;
semwait(&MHwait,0);
for(i = 0; i < ifp->Hmax; i++) {
if(addreq(lp->addr,addr)) {
return lp;
}
if (lp->time == 0)
break;
lp++;
}
return NULLLQ;
}
/*----------------------------------------------------------------------*
* Create a new entry in the AX.25 link table *
*-----------------------------------------------------------------------*/
static struct lq * near
al_create(struct iface *ifp)
{
struct lq *lp, *lo = NULLLQ;
int32 time = currtime;
semwait(&MHwait,1);
if (ifp->Hcurrent < ifp->Hmax) {
lp = &ifp->lq[ifp->Hcurrent];
ifp->Hcurrent++;
} else { /* look for the oldest one */
int i;
lp = ifp->lq;
for (i = 0; i < ifp->Hcurrent; i++) {
if (lp->time < time) {
time = lp->time;
lo = lp;
}
lp++;
}
lp = lo;
}
semrel(&MHwait);
return lp;
}
/*----------------------------------------------------------------------*
* Log the address of an incoming packet *
*-----------------------------------------------------------------------*/
static void near
logaddr(struct iface *ifp,char *addr,char pid)
{
struct lq *lp;
if(MHwait || addreq(addr,ifp->hwaddr)) {
/* Don't log our own packets if we hear them */
return;
}
if((lp = al_lookup(ifp,addr)) == NULLLQ &&
(lp = al_create(ifp)) == NULLLQ) {
return;
}
memcpy(lp->addr,addr,AXALEN);
lp->pid = pid;
lp->currxcnt++;
lp->time = currtime;
}
/*----------------------------------------------------------------------*
*-----------------------------------------------------------------------*/
int
axflush(struct iface *ifp)
{
semwait(&MHwait,1);
ifp->rawsndcnt = ifp->Hcurrent = 0;
memset(ifp->lq,0,ifp->Hmax * sizeof(struct lq)); /* brute force */
semrel(&MHwait);
return 0;
}
#endif